import math
import FCMatrix, FCMathUtil

from maya import cmds
from maya import OpenMaya as om


class Model:
    idx = 0
    camera = None

    near = 12000
    far = 15000

    def __init__( self, _isLod, _quality = 'none' ):
        Model.idx += 1
        if (cmds.ls('antCrowd_cam_*') != []) and (cmds.objExists(cmds.ls('antCrowd_cam_*')[0])):
            Model.camera = cmds.ls('antCrowd_cam_*')[0]
        else:
            Model.camera = 'persp'

        self.number = Model.idx
        self.node = None
        self.parent = None
        if _isLod:
            self.parent = cmds.createNode( 'transform', name = 'ant_behavior' + str(Model.idx) )

        self.low = None
        self.middle = None
        self.high = None
        self.quality = _quality
        self.isLod = _isLod

        self.showLow = False
        self.showMiddle = True
        self.showHigh = False

        self.avgMasterLeg = [ MVector() for _cnt in xrange(3) ]
        self.avgTargetLeg = [ MVector() for _cnt in xrange(3) ]

        self.main = None
        self.masterLeg = None
        self.targetLeg = None
        self.restMat = [0, 0, 0]
        self.isLeft = True

        self.step = 0.0
        self.avgDist = 1.0
        self.mid = 1.0
        self.height = 1.0
        self.drive = 1.0
        self.maxUp = 20.0

        self.createModel()
        
    def createModel( self ):
        if self.isLod:
            self.low = cmds.instance( 'ant_low_all', st = True )
            self.middle = cmds.instance( 'ant_mid_all', st = True )
            self.high = cmds.duplicate( 'ant_hi_all', rc = 1, un = 1 )
            self.main =  self.findLeg( self.high )
            cmds.parent( self.low, self.parent, relative = 1 )
            cmds.parent( self.middle, self.parent, relative = 1 )
            cmds.parent( self.high[0], self.parent, relative = 1 )
            
            self.node = self.parent
        else:
            if 'low' == self.quality:
                self.low = cmds.instance( 'ant_low_all', st = True )
                self.node = self.low
                cmds.editDisplayLayerMembers ('antLow', self.low[0])
            elif 'middle' == self.quality:
                self.middle = cmds.instance( 'ant_mid_all', st = True )
                self.node = self.middle
                cmds.editDisplayLayerMembers ('antMiddle', self.middle[0])
            elif 'high' == self.quality:
                self.high = cmds.duplicate( 'ant_hi_all', rc = 1, un = 1 )
                self.main =  self.findLeg( self.high )
                self.node = self.main
                cmds.editDisplayLayerMembers ('antHigh', self.high[0])

    def showModel( self, _quality ):
        if _quality == self.quality:
            return

        self.quality = _quality

        if 'low' == self.quality:
            cmds.setAttr( self.low[0] + '.visibility', 1 )
            cmds.setAttr( self.middle[0] + '.visibility', 0 )
            cmds.setAttr( self.main[0] + '.visibility', 0 )
            self.showLow = True
            self.showMiddle = False
            self.showHigh = False
            self.node = self.low
        elif 'middle' == self.quality:
            cmds.setAttr( self.low[0] + '.visibility', 0 )
            cmds.setAttr( self.middle[0] + '.visibility', 1 )
            cmds.setAttr( self.main[0] + '.visibility', 0 )
            self.showLow = False
            self.showMiddle = True
            self.showHigh = False
            self.node = self.middle
        elif 'high' == self.quality:
            cmds.setAttr( self.low[0] + '.visibility', 0 )
            cmds.setAttr( self.middle[0] + '.visibility', 0 )
            cmds.setAttr( self.main[0] + '.visibility', 1 )
            self.showLow = False
            self.showMiddle = False
            self.showHigh = True
            self.node = self.main

    def distanceFromCamera( self ):
        _cameraPos = cmds.xform( Model.camera, ws=1, t=1, q=1 )
        _cameraPoint = om.MPoint( _cameraPos[0], _cameraPos[1], _cameraPos[2] )
        _position = cmds.getAttr( self.parent + '.t' )[0]    
        _targetPoint = om.MPoint( _position[0], _position[1], _position[2] )

        return _targetPoint.distanceTo( _cameraPoint )

    def update( self, _tx, _ty, _tz, _rx, _ry, _rz, _frame ):
        cmds.setAttr( self.node[0] + '.tx', _tx )
        cmds.setAttr( self.node[0] + '.ty', _ty )
        cmds.setAttr( self.node[0] + '.tz', _tz )

        cmds.setAttr( self.node[0] + '.rx', _rx )
        cmds.setAttr( self.node[0] + '.ry', _ry )
        cmds.setAttr( self.node[0] + '.rz', _rz )

        if self.isLod:            
            
            if self.distanceFromCamera() < Model.near :
                self.showModel( 'low' )
            elif Model.near < self.distanceFromCamera() < Model.far:
                self.showModel( 'middle' )
            else:
                self.showModel( 'high' )

            cmds.setKeyframe( self.low[0], attribute = 'visibility', value = self.showLow, t = _frame )
            cmds.setKeyframe( self.middle[0], attribute = 'visibility', value = self.showMiddle, t = _frame )
            cmds.setKeyframe( self.high[0], attribute = 'visibility', value = self.showHigh, t = _frame )

        if 'high' == self.quality:
                FCMatrix.getMtoTV( cmds.getAttr( self.masterLeg[0] + '.worldMatrix' ), self.avgMasterLeg[0] )
                FCMatrix.getMtoTV( cmds.getAttr( self.masterLeg[2] + '.worldMatrix' ), self.avgMasterLeg[1] )
                FCMatrix.getMtoTV( cmds.getAttr( self.masterLeg[4] + '.worldMatrix' ), self.avgMasterLeg[2] )

        cmds.setKeyframe( self.node[0], attribute = 'translateX', value = _tx, t=_frame )
        cmds.setKeyframe( self.node[0], attribute = 'translateY', value = _ty, t=_frame  )
        cmds.setKeyframe( self.node[0], attribute = 'translateZ', value = _tz, t=_frame )
        cmds.setKeyframe( self.node[0], attribute = 'rotateX', value = _rx, t=_frame )
        cmds.setKeyframe( self.node[0], attribute = 'rotateY', value = _ry, t=_frame )
        cmds.setKeyframe( self.node[0], attribute = 'rotateZ', value = _rz, t=_frame )

    def updateWalk( self, _frame, _speed ):
        if 'high' != self.quality:
            return

        if _frame <= 1:
            self.setTarget( 0 )
            self.setTarget( 1 )
            self.setTarget( 2 )
            self.setTarget( 3 )
            self.setTarget( 4 )
            self.setTarget( 5 )

            self.resetAvgLeg()
            self.storeMasterLeg()

            self.avgDist = 1.0
            self.mid = 1.0
            self.height = 1.0
        else:
            self.setAvgDist( _speed )
            self.setStepDist()

            #if self.number == 1:
                #print 'AvgDist', self.getAvgDist()
                #print 'StepDist', self.getStepDist()
                #print 'Mid', self.mid

            if self.getAvgDist() >= self.getStepDist():
                self.setTarget( 0, 0.0, _frame )
                self.setTarget( 2, 0.0, _frame )
                self.setTarget( 4, 0.0, _frame )
                self.isLeft = False
                self.mid = 1.0
                self.resetAvgDist()
                self.storeMasterLeg()
                #if self.number == 1:
                    #print 'left -> right'

            if self.getAvgDist() >= (self.getStepDist() / 2.0) and self.mid <= 1.5:
                self.setTarget( 1, 0.0, _frame )
                self.setTarget( 3, 0.0, _frame )
                self.setTarget( 5, 0.0, _frame )
                self.isLeft = True
                self.mid = 2.0
                self.storeMasterLeg()
                #if self.number == 1:
                    #print 'right -> left'

            _step = self.getAvgDist() / (self.stepDist / self.mid)

            if self.isLeft:
                self.setTarget( 0, _step, _frame )
                self.setTarget( 2, _step, _frame )
                self.setTarget( 4, _step, _frame )
                cmds.xform( self.targetLeg[1], m = self.restMat[0] )
                cmds.xform( self.targetLeg[3], m = self.restMat[1] )
                cmds.xform( self.targetLeg[5], m = self.restMat[2] )
            else:
                self.setTarget( 1, _step, _frame )
                self.setTarget( 3, _step, _frame )
                self.setTarget( 5, _step, _frame )
                cmds.xform( self.targetLeg[0], m = self.restMat[0] )
                cmds.xform( self.targetLeg[2], m = self.restMat[1] )
                cmds.xform( self.targetLeg[4], m = self.restMat[2] )
                    
            self.setStepDist()

        if self.number == 1:
            print 'AvgDist', self.getAvgDist()

        for _targetLeg in self.targetLeg:
            cmds.setKeyframe( _targetLeg + '.t', t = _frame )
            cmds.setKeyframe( _targetLeg + '.r', t = _frame )
            
    def findLeg( self, _model ):
        _main = [ main for main in cmds.listRelatives( _model, children = True ) if 'main' in main ] 
        self.masterLeg = [ target for target in cmds.listRelatives( _main, children = True ) if 'null' in target ]
        _targetGroup = [ group for group in cmds.listRelatives( _model, children = True ) if 'leg_grp' in group ]
        self.targetLeg = cmds.listRelatives( _targetGroup[0], children = True )

        return _main

    def setTarget( self, _idx, _step = 0.0, _frame = 0 ):
        _masterMat = cmds.getAttr( self.masterLeg[_idx] + '.worldMatrix', t = _frame )
        cmds.xform( self.targetLeg[_idx], m = _masterMat )
        
        _up = self.maxUp * math.sin( (math.pi / 2.0) * _step )
        
        #if self.number == 1:
            #print _up, _step
        
        _height = getMtoT( _masterMat )[1]
        cmds.setAttr( self.targetLeg[_idx] +'.ty', _height + _up )

    def resetAvgLeg( self ):
        self.avgTargetLeg[0].x = self.avgMasterLeg[0].x
        self.avgTargetLeg[0].y = self.avgMasterLeg[0].y
        self.avgTargetLeg[0].z = self.avgMasterLeg[0].z

        self.avgTargetLeg[1].x = self.avgMasterLeg[1].x
        self.avgTargetLeg[1].y = self.avgMasterLeg[1].y
        self.avgTargetLeg[1].z = self.avgMasterLeg[1].z

        self.avgTargetLeg[2].x = self.avgMasterLeg[2].x
        self.avgTargetLeg[2].y = self.avgMasterLeg[2].y
        self.avgTargetLeg[2].z = self.avgMasterLeg[2].z

    def storeMasterLeg( self ):
        if self.isLeft:
            self.restMat[0] = cmds.getAttr( self.masterLeg[1] + '.worldMatrix' )
            self.restMat[1] = cmds.getAttr( self.masterLeg[3] + '.worldMatrix' )
            self.restMat[2] = cmds.getAttr( self.masterLeg[5] + '.worldMatrix' )
        else:
            self.restMat[0] = cmds.getAttr( self.masterLeg[0] + '.worldMatrix' )
            self.restMat[1] = cmds.getAttr( self.masterLeg[2] + '.worldMatrix' )
            self.restMat[2] = cmds.getAttr( self.masterLeg[4] + '.worldMatrix' )

    def getDist( self, _idx ):
        #_xLength = self.avgTargetLeg[_idx].x - self.avgMasterLeg[_idx].x
        #_yLength = self.avgTargetLeg[_idx].y - self.avgMasterLeg[_idx].y
        #_zLength = self.avgTargetLeg[_idx].z - self.avgMasterLeg[_idx].z
        #
        #_xyLength = math.sqrt( _xLength * _xLength ) * math.sqrt( _yLength * _yLength )
        #_xyzLength = math.sqrt( _xyLength * _xyLength ) * math.sqrt( _zLength * _zLength )
        #
        #return _xyzLength
        
        _target = om.MPoint( self.avgTargetLeg[_idx].x, self.avgTargetLeg[_idx].y, self.avgTargetLeg[_idx].z )
        _master = om.MPoint( self.avgMasterLeg[_idx].x, self.avgMasterLeg[_idx].y, self.avgMasterLeg[_idx].z )
        
        return _master.distanceTo( _target )

    def setAvgDist( self, _speed ):
        _dist = ( ( self.getDist(0) + self.getDist(1) + self.getDist(2) ) ) / _speed 
        self.avgDist += _dist
        
        self.resetAvgLeg()
        
    def getAvgDist( self ):
        return self.avgDist - 1.0
        
    def resetAvgDist( self ):
        self.avgDist = 1.0
        
    def getStepDistAlways( self ):
        self.stepDist = self.height / 2.0
        return self.stepDist
        
    def setStepDist( self ):
        self.stepDist = self.getStepDistAlways()
        
    def getStepDist( self ):
        return self.stepDist